package cn.kimming.service.impl;

import cn.kimming.common.constant.SystemConstant;
import cn.kimming.common.dto.CheckInDTO;
import cn.kimming.common.dto.CheckOutDTO;
import cn.kimming.common.exception.SystemException;
import cn.kimming.common.pojo.*;
import cn.kimming.common.util.DateUtil;
import cn.kimming.common.vo.CheckInVO;
import cn.kimming.common.vo.CheckOutVO;
import cn.kimming.common.vo.OrderbillVO;
import cn.kimming.mapper.*;
import cn.kimming.service.IOrderbillService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author 刘铭轩
 * date 2021-06-09
 */
@Service
@Transactional
public class OrderbillServiceImpl implements IOrderbillService {

    @Autowired
    private RoomPlanMapper roomPlanMapper;
    @Autowired
    private RoomTypeMapper roomTypeMapper;
    @Autowired
    private OrderbillMapper orderbillMapper;
    @Autowired
    private OrderbillItemMapper orderbillItemMapper;
    @Autowired
    private RoomMapper roomMapper;
    @Autowired
    private CheckInfoMapper checkInfoMapper;

    /**
     * 预定房型
     * @param bo
     */
    @Override
    public synchronized void reserve(Orderbill bo) {
        /*
            1. 检查订单项的入住时间和离店时间是否合法
                 最大离店时间 = 今天 + 20天
                (1)入住时间 < 离店时间
                (2)入住时间 >= 今天 && 入住时间 < 最大离店时间
                (3)离店时间 <= 最大离店时间
            2. 检查每个订单项库存是否充足
                每个订单项预定的房型从[入店时间, 最大离店时间)的每一天库存量都要>quantity
            3. 操作库存
            4. 完善订单信息, 保存订单
            5. 完善订单项信息
                订单项小计 = 订单项单价 * 数量
                订单项单价 = 房型今日价格 * 折扣

            注意: 某订单项如从2021-01-01入住, 2021-01-03离店, 订单项的单价只取决于 2021-01-01 即入住当天
         */

        // 检查入店和离店时间
        Date checkInTime = bo.getCheckInTime();
        Date checkOutTime = bo.getCheckOutTime();
        if (checkInTime.compareTo(checkOutTime) >= 0) throw new SystemException("入住时间必须小于离店时间");
        Date minInDate = DateUtil.getAfterDate(0);
        Date maxLeaveDate = DateUtil.getAfterDate(20);
        if (checkInTime.compareTo(minInDate) < 0 || checkInTime.compareTo(maxLeaveDate) >= 0) throw new SystemException("入住时间必须在20天之内");
        if (checkOutTime.compareTo(maxLeaveDate) > 0) throw new SystemException("离店时间不能超过" + DateUtil.date2String(maxLeaveDate));

        // 检查是否重复预约
        String orderId = orderbillMapper.findOrderIdByPhone(bo.getPhone());
        if (orderId != null) throw new SystemException("该手机号存在预约中的订单, 无法重复预约");

        for (OrderbillItem item : bo.getOrderbillItems()) {
            // 检查库存
            List<Date> dates = DateUtil.getBetweenDates(checkInTime, checkOutTime);
            for (int i = 0; i < dates.size() - 1; i++) {
                Date date = dates.get(i);
                RoomPlan roomPlan = roomPlanMapper.findByTypeIdAndDate(item.getRoomTypeId(), date);
                if (roomPlan == null || roomPlan.getRepertory() < item.getQuantity()) { // 库存不足
                    RoomType roomType = roomTypeMapper.selectByPrimaryKey(item.getRoomTypeId());
                    throw new SystemException(roomType.getTypeName() + "在" + DateUtil.date2String(date) + "的库存量不足" + item.getQuantity());
                }
                // 操作库存
                roomPlan.setRepertory(roomPlan.getRepertory() - item.getQuantity());
                roomPlanMapper.updateByFutureDateAndTypeId(roomPlan);

                // 设置订单项单价
                if (i == 0) {
                    item.setPrice(roomPlan.getPrice().multiply(new BigDecimal(roomPlan.getDiscount())));
                }
            }
        }

        // 完善订单信息, 保存订单
        bo.setInsertTime(new Date());   // 设置订单生成时间
        bo.setUpdateTime(new Date());   // 设置订单修改时间
        bo.setOrderStatus(SystemConstant.ORDERBILL_STATUS_RESERVED); // 设置订单状态
        bo.setOrderId(DateUtil.genOrderId());    // 设置订单号
        orderbillMapper.insertSelective(bo);

        for (OrderbillItem item : bo.getOrderbillItems()) {
            item.setOrderbillId(bo.getId());
            orderbillItemMapper.insertSelective(item);
        }
    }

    @Override
    public PageInfo<OrderbillVO> findInfo(int page, String phone) {
        PageHelper.startPage(page, 10);
        List<OrderbillVO> list = orderbillMapper.findInfo(phone);
        PageInfo<OrderbillVO> info = new PageInfo<>(list);
        return info;
    }

    @Override
    public String getOrderIdByPhone(String phone) {
        String orderId = orderbillMapper.findOrderIdByPhone(phone);
        if (orderId == null) throw new SystemException("没有对应的订单记录");
        return orderId;
    }

    @Override
    public CheckInVO findCheckInVOByOrderId(String orderId) {
        return orderbillMapper.findCheckInVOByOrderId(orderId);
    }


    /**
     * 1.检查订单信息
     *      (1)是否为预约状态
     *      (2)入住日期是否为今天
     * 2.查询订单项
     *      (1)判断预定房型的数量是否与身份证号数量相等
     * 3.尝试分配房间, 插入入住信息
     *      (1)修改房间入住状态和打扫状态
     * 4.完善订单信息
     * @param dto
     * @return
     */
    @Override
    public Set<Integer> checkIn(CheckInDTO dto) {
        // 检查订单信息
        Long orderbillId = dto.getOrderbillId();
        Orderbill orderbill = orderbillMapper.selectByIdAndCheckIn(orderbillId);
        if (orderbill == null) throw new SystemException("没有此预定订单信息, 无法登记入住");
        Date date = orderbill.getCheckInTime();
        Date today = DateUtil.getAfterDate(0);
        if (today.compareTo(date) > 0) throw new SystemException("未到入住日期, 无法登记入住");
        // 检查订单项
        List<OrderbillItem> items = orderbillItemMapper.selectAllByOrderbillId(orderbillId);
        List<Long> checkInRoomTypeIds = new ArrayList<>();  // 记录要入住的房型ID, 考虑一个房型预定了多个数量, 不能去重
        for (OrderbillItem item : items) {
            for (int i = 0; i < item.getQuantity(); i++) {
                checkInRoomTypeIds.add(item.getRoomTypeId());
            }
        }
        List<String> idCards = dto.getIdCards();
        if (checkInRoomTypeIds.size() > idCards.size()) {    // 登记身份证数量小于入住房间数量
            throw new SystemException("入住人员登记的身份证数量至少为" + checkInRoomTypeIds.size());
        }
        // 分配房间
        Set<Integer> roomNums = new HashSet<>();
        for (Long typeId : checkInRoomTypeIds) {
            Room room = roomMapper.selectCheckinable(typeId);
            if (room == null) {
                RoomType type = roomTypeMapper.selectByPrimaryKey(typeId);
                throw new SystemException(type.getTypeName() + "没有可入住的房间, 请尽快联系清洁工打理已退房的房间");
            }
            // 生成入住信息
            CheckInfo checkInfo = CheckInfo.builder()
                    .checkInTime(new Date())
                    .idCard(idCards.remove(0))
                    .orderbillId(dto.getOrderbillId())
                    .roomId(room.getId())
                    .flag(SystemConstant.CHECK_INFO_FLAG_ON)
                    .build();
            checkInfoMapper.insertSelective(checkInfo);
            // 修改房间状态
            room.setLiveStatus(SystemConstant.ROOM_LIVE_STATUS_LIVING);
            room.setCleanStatus(SystemConstant.ROOM_CLEAN_STATUS_UNCLEAN);
            roomMapper.updateByPrimaryKeySelective(room);
            // 记录入住的房间编号
            roomNums.add(room.getRoomNum());
        }
        // 修改订单信息
        orderbill.setOrderStatus(SystemConstant.ORDERBILL_STATUS_LIVING);
        orderbill.setDepositPrice(dto.getDepositPrice());
        orderbill.setUpdateTime(new Date());
        orderbillMapper.updateByPrimaryKeySelective(orderbill);
        return roomNums;
    }

    @Override
    public CheckOutVO findCheckOutVOByRoomNum(Integer roomNum) {
        CheckInfo checkInfo = checkInfoMapper.findLivingByRoomNum(roomNum);
        // 查询房间是否有人在入住中
        if (checkInfo == null) throw new SystemException("没有对应的入住订单");
        // 获取订单号, 查询订单信息
        Long orderbillId = checkInfo.getOrderbillId();
        CheckOutVO checkOutVO = orderbillMapper.findCheckOutVOById(orderbillId);
        return checkOutVO;
    }

    /**
     * 退房结账
     * 1. 判断订单状态
     * 2. 修改订单对应的入住登记信息
     * 3. 修改房间入住信息
     * 4. 完善订单信息
     * @param bo
     * @return
     */
    @Override
    public BigDecimal checkout(CheckOutDTO bo) {
        // 判断订单状态
        Orderbill orderbill = orderbillMapper.selectByOrderId(bo.getOrderId());
        if (orderbill == null) throw new SystemException("没有此订单记录");
        if (orderbill.getOrderStatus() != SystemConstant.ORDERBILL_STATUS_LIVING) throw new SystemException("订单不在入住状态中");

        // 修改订单对应的入住信息
        List<CheckInfo> infos = checkInfoMapper.selectLivingByOrderbillId(orderbill.getId());
        Date date = new Date();

        for (CheckInfo info : infos) {
            info.setCheckOutTime(date);
            info.setFlag(SystemConstant.CHECK_INFO_FLAG_OFF);
            checkInfoMapper.updateByPrimaryKeySelective(info);
            // 修改房间入住状态
            Long roomId = info.getRoomId();
            Room room = Room.builder().id(roomId).liveStatus(SystemConstant.ROOM_LIVE_STATUS_NONE).build();
            roomMapper.updateByPrimaryKeySelective(room);
        }

        // 完善订单信息
        Date curDate = DateUtil.getAfterDate(0);
        Date checkOutTime = orderbill.getCheckOutTime();
        short status = curDate.compareTo(checkOutTime) > 0 ? SystemConstant.ORDERBILL_STATUS_OVERTIME : SystemConstant.ORDERBILL_STATUS_FINISHED;
        BigDecimal roomFee = orderbillMapper.findRoomFeeById(orderbill.getId());
        BigDecimal totalPrice = roomFee.add(bo.getAddPrice());
        orderbill.setAddPrice(bo.getAddPrice());    // 记录附加费用
        orderbill.setUpdateTime(date);              // 修改更新时间
        orderbill.setTotalPrice(totalPrice);        // 记录订单总金额
        orderbill.setOrderStatus(status);           // 修改订单状态
        orderbillMapper.updateByPrimaryKeySelective(orderbill);
        return totalPrice;
    }
}
